package com.cherrypt.api.core.service.impl;

import com.cherrypt.api.common.enums.ResultEnum;
import com.cherrypt.api.common.exception.TipException;
import com.cherrypt.api.model.bo.RegisterBo;
import com.cherrypt.api.model.bo.UserBo;
import com.cherrypt.api.model.dataobject.UserInfo;
import com.cherrypt.api.model.dataobject.UserLogin;
import com.cherrypt.api.model.dto.UserDto;
import com.cherrypt.api.model.vo.RegisterVo;
import com.cherrypt.api.core.service.UserInfoService;
import com.cherrypt.api.core.service.UserLoginService;
import com.cherrypt.api.core.service.UserService;
import com.cherrypt.api.common.util.CheckUtils;
import com.cherrypt.api.core.util.CherryUtils;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;

import java.time.Duration;

@Service
public class UserServiceImpl implements UserService {
    private static final Logger logger = LoggerFactory.getLogger(UserServiceImpl.class);

    @Autowired
    private ValueOperations<String, Object> valueOperations;

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private UserLoginService userLoginService;

    @Autowired
    private UserInfoService userInfoService;

    @Value("${api.config.redis.activationCode.prefix}")
    private String activationCodePrefix;

    @Value("${api.config.redis.activationCode.exp}")
    private long activationCodeExp;

    @Value("${api.config.redis.resetPassCode.prefix}")
    private String resetPassCodePrefix;

    @Value("${api.config.redis.resetPassCode.exp}")
    private long resetPassCodeExp;

    /**
     * 创建用户
     * 1. 插入用户登录表
     * 2. 获取插入的用户登录表id
     * 3. 插入用户信息表
     *
     * @param registerVO registerVO
     * @return save true
     * @throws TipException
     */
    @Override
    public boolean saveUser(RegisterVo registerVO) throws TipException, DataAccessException {
        CheckUtils.checkEmailAndUsername(registerVO.getEmail(), registerVO.getUsername());
        CheckUtils.checkPassword(registerVO.getPassword());
        // md5密码
        registerVO.setPassword(DigestUtils.md5Hex(CherryUtils.config.getPassPrefix() + registerVO.getPassword()));
        try {
            // 判断是否插入了用户登录表
            if (userLoginService.saveUserLogin(RegisterBo.getUserLogin(registerVO))) {
                UserLogin userLogin = userLoginService.getUserLoginByEmail(registerVO.getEmail());
                if (userLogin != null) {
                    // 如果插入了就根据email获取插入的id
                    // 插入用户信息表
                    return userInfoService.saveUserInfo(RegisterBo.getUserInfo(registerVO, userLogin.getId(), CherryUtils.randomCode()));
                }
            }
            throw new TipException(ResultEnum.ACCOUNT_CREATE_ERROR);
        } catch (DataAccessException e) {
            logger.info("重复注册, 唯一键约束, email: {}, nickName: {}", registerVO.getEmail(), registerVO.getUsername());
            throw new TipException(ResultEnum.REGISTER_ERROR);
        }
    }

    /**
     * 根据邮箱和密码登录
     * 检查是否激活了账号
     * 检查账号是否被ban
     * 检查邮箱和密码是否符合规则
     *
     * @param email    email
     * @param password password
     * @return 用户
     * @throws TipException
     */
    @Override
    public UserDto getUserByLogin(String email, String password) throws TipException {
        CheckUtils.checkEmail(email);
        CheckUtils.checkPassword(password);
        // 登录
        // 根据登录结果获取用户信息
        // 这里不用判断null，因为在userLoginService.login里面判断过一次了
        UserLogin userLogin = userLoginService.login(email, password);
        UserInfo userInfo = userInfoService.getUserInfoByUid(userLogin.getId());
        return UserBo.getUserDto(userLogin, userInfo, CherryUtils.getNewToken(userLogin.getId()));
    }

    @Override
    public UserDto getUserById(int uid) throws TipException {
        UserLogin userLogin = userLoginService.getUserLoginByUid(uid);
        if (userLogin == null) {
            throw new TipException(ResultEnum.USER_EMPTY_ERROR);
        }
        UserInfo userInfo = userInfoService.getUserInfoByUid(userLogin.getId());
        return UserBo.getUserDto(userLogin, userInfo, CherryUtils.getNewToken(userLogin.getId()));
    }

    @Override
    public UserDto getFriendById(int uid) {
        UserInfo userInfo = userInfoService.getFriendByUid(uid);
        return UserBo.getUserDto(null, userInfo, null);
    }

    /**
     * redis中插入激活码
     *
     * @param code 激活码
     * @throws TipException
     */
    @Override
    public void saveActivationCode(String code, String email) throws TipException {
        CheckUtils.checkEmail(email);
        if (StringUtils.isBlank(code)) {
            throw new TipException(ResultEnum.ACTIVATION_CODE_EMPTY_ERROR);
        } else {
            valueOperations.set(activationCodePrefix + code, email, Duration.ofSeconds(activationCodeExp));
        }
    }

    /**
     * 激活账号
     * 激活了后删除该值
     *
     * @param code code
     * @return
     * @throws TipException
     */
    @Override
    public boolean activateUser(String code) throws TipException {
        if (StringUtils.isBlank(code)) {
            throw new TipException(ResultEnum.ACTIVATION_CODE_EMPTY_ERROR);
        }
        String email = (String) valueOperations.get(activationCodePrefix + code);
        if (StringUtils.isBlank(email)) {
            throw new TipException(ResultEnum.ACTIVATION_CODE_ERROR);
        }
        boolean isActivation = userLoginService.activeUserLogin(email);
        if (isActivation) {
            redisTemplate.delete(activationCodePrefix + code);
        }
        return isActivation;
    }

    @Override
    public void saveResetPassCode(String code, String email) throws TipException {
        if (StringUtils.isBlank(code)) {
            throw new TipException(ResultEnum.RESET_PASS_CODE_EMPTY_ERROR);
        } else {
            valueOperations.set(resetPassCodePrefix + code, email, Duration.ofSeconds(resetPassCodeExp));
        }
    }

    @Override
    public String existResetPassCode(String code) throws TipException {
        if (StringUtils.isBlank(code)) {
            throw new TipException(ResultEnum.RESET_PASS_CODE_EMPTY_ERROR);
        }
        String email = (String) valueOperations.get(resetPassCodePrefix + code);
        if (StringUtils.isBlank(email)) {
            throw new TipException(ResultEnum.RESET_PASS_CODE_ERROR);
        }
        return email;
    }

    @Override
    public void removeResetPassCode(String code) {
        redisTemplate.delete(resetPassCodePrefix + code);
    }
}
